package lock;

import lombok.Data;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @description: CLHLock锁：通过队列+非cas自旋实现锁
 * 参考： java高并发核心编程
 * @author: kanggw
 * @date: 2023/6/12
 **/
public class CLHLock implements Lock {
    /**
     * 1. 实现lock类
     * 2. 实现一个节点类作为队列使用
     * 3. 定义类成员变量
     *  3.1 threadLoal用来标识当前节点的线程本地变量
     *  3.2 node节点
     * 4. 实现lock与unlock
     */
    private static ThreadLocal<Node> curNodeLocal = new ThreadLocal<>();

    private Node node;

    private AtomicReference<Node> tail = new AtomicReference<>();

    public CLHLock() {
        tail.getAndSet(Node.EMPTY);
    }

    @Override
    public void lock() {
        /**
         * 1. 新建一个空节点
         * 2. 申请锁的资源首先通过cas方式添加到队列中，如果失败则cas自旋重试
         * 2. 添加成功后则通过普通的自旋判断前节点是否处于locked状态，如果非locked状态则当前节点获取到锁，如果locked状态则自旋
         */
        Node curNode = new Node(null, true);
        Node preNode = this.tail.get();
        while (!tail.compareAndSet(preNode, curNode)) {
            preNode = tail.get();
        }
        curNode.setPreNode(preNode);
        //判断前驱节点是否获取到锁
        while (curNode.getPreNode().isLocked()) {
            System.out.println(Thread.currentThread().getName() + "没有获取到锁，释放cpu资源");
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"获取到了锁");
        curNodeLocal.set(curNode);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        Node curNode = curNodeLocal.get();
        curNode.setLocked(false);
        curNode.setPreNode(null);
        curNodeLocal.set(null);
    }

    @Override
    public Condition newCondition() {
        return null;
    }

    @Data
    static class Node{
        Node preNode;
        volatile boolean locked;

        public Node(Node preNode, boolean locked) {
            this.preNode = preNode;
            this.locked = locked;
        }

        public static final Node EMPTY = new Node(null, false);
    }

}
